package com.kedzie.vbox.machine.settings;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.kedzie.vbox.R;
import com.kedzie.vbox.VBoxApplication;
import com.kedzie.vbox.api.IMachine;
import com.kedzie.vbox.api.IMedium;
import com.kedzie.vbox.api.IStorageController;
import com.kedzie.vbox.api.ISystemProperties;
import com.kedzie.vbox.api.jaxb.ChipsetType;
import com.kedzie.vbox.api.jaxb.DeviceType;
import com.kedzie.vbox.api.jaxb.IMediumAttachment;
import com.kedzie.vbox.api.jaxb.StorageBus;
import com.kedzie.vbox.app.Utils;
import com.kedzie.vbox.task.ActionBarTask;
import com.kedzie.vbox.task.DialogTask;
/**
* Expandable list of storage controllers and associated attachments
*
* @apiviz.stereotype Fragment
*/
public class StorageListFragment extends SherlockFragment {
/**
* Listener for Storage Controller clicks
*/
public static interface OnStorageControllerClickedListener {
/**
* @param element the {@link IStorageController} click event
*/
public void onStorageControllerClicked(IStorageController element);
}
/**
* Listener for Medium attachment clicks
*/
public static interface OnMediumAttachmentClickedListener {
/**
* Handle Medium attachment click event
* @param element the {@link IMediumAttachment} click event
*/
public void onMediumAttachmentClicked(IMediumAttachment element);
}
private ExpandableListView _listView;
private ItemAdapter _listAdapter;
private OnStorageControllerClickedListener _controllerListener;
private OnMediumAttachmentClickedListener _mediumListener;
private IMachine _machine;
private ISystemProperties _systemProperties;
private ArrayList<IStorageController> _controllers;
private ListMultimap<IStorageController, IMediumAttachment> _data;
private Map<IStorageController, List<IMediumAttachment>> _dataListMap;
/**
* Load Data
*/
private class LoadDataTask extends DialogTask<IMachine, Void> {
public LoadDataTask() {
super(getSherlockActivity(), _machine.getAPI(), R.string.progress_load_storage_controllers);
}
@Override
protected Void work(IMachine... params) throws Exception {
_systemProperties = _vmgr.getVBox().getSystemProperties();
ChipsetType chipset = _machine.getChipsetType();
for(StorageBus bus : StorageBus.values()) {
_systemProperties.getMaxInstancesOfStorageBus(chipset, bus);
_systemProperties.getDeviceTypesForStorageBus(bus);
}
_controllers = params[0].getStorageControllers();
_data = ArrayListMultimap.create();
_dataListMap = new HashMap<IStorageController, List<IMediumAttachment>>();
for(IStorageController c : _controllers) {
params[0].clearCacheNamed("getMediumAttachmentsOrController-"+c.getName());
c.getBus(); c.getControllerType();
ArrayList<IMediumAttachment> attachments = params[0].getMediumAttachmentsOfController(c.getName());
_data.putAll(c, attachments);
for(IMediumAttachment a : attachments) {
if(a.getMedium()!=null) {
a.getMedium().clearCache();
a.getMedium().getName();
a.getMedium().getBase().getName();
}
}
_dataListMap.put(c, attachments);
}
return null;
}
@Override
protected void onSuccess(Void result) {
super.onSuccess(result);
_listAdapter = new ItemAdapter(getSherlockActivity(), _controllers, _data);
_listView.setAdapter(_listAdapter);
for(int i=0; i<_controllers.size(); i++)
_listView.expandGroup(i);
}
}
/**
* Add Controller
*/
private class AddControllerTask extends ActionBarTask<StorageBus, IStorageController> {
public AddControllerTask() {
super(getSherlockActivity(), _machine.getAPI());
}
@Override
protected IStorageController work(StorageBus... params) throws Exception {
IStorageController controller = _machine.addStorageController(params[0].toString(), params[0]);
controller.getName(); controller.getBus(); controller.getControllerType();
return controller;
}
@Override
protected void onSuccess(IStorageController result) {
super.onSuccess(result);
_controllers.add(result);
_dataListMap.put(result, new ArrayList<IMediumAttachment>());
_listAdapter.notifyDataSetChanged();
}
}
/**
* Delete Controller
*/
private class DeleteControllerTask extends ActionBarTask<IStorageController, IStorageController> {
public DeleteControllerTask() {
super(getSherlockActivity(), _machine.getAPI());
}
@Override
protected IStorageController work(IStorageController... params) throws Exception {
_machine.removeStorageController(params[0].getName());
return params[0];
}
@Override
protected void onSuccess(IStorageController result) {
super.onSuccess(result);
_controllers.remove(result);
_data.removeAll(result);
_dataListMap.remove(result);
_listAdapter.notifyDataSetChanged();
}
}
/**
* Detach Medium
*/
private class DetachMediumTask extends ActionBarTask<IMediumAttachment, IMediumAttachment> {
public DetachMediumTask() {
super(getSherlockActivity(), _machine.getAPI());
}
@Override
protected IMediumAttachment work(IMediumAttachment... params) throws Exception {
_machine.detachDevice(params[0].getController(), params[0].getPort(), params[0].getDevice());
return params[0];
}
@Override
protected void onSuccess(IMediumAttachment result) {
super.onSuccess(result);
IStorageController controller = null;
for(IStorageController c : _controllers)
if(c.getName().equals(result.getController()))
controller=c;
_data.remove(controller, result);
_dataListMap.get(controller).remove(result);
_listAdapter.notifyDataSetChanged();
}
}
/**
* Move the medium to a different slot within controller.
*/
abstract class ListMediumsTask extends ActionBarTask<Void, List<IMedium>> {
private IStorageController controller;
private DeviceType deviceType;
public ListMediumsTask(IStorageController controller, DeviceType type) {
super(getSherlockActivity(), _machine.getAPI());
this.controller = controller;
this.deviceType = type;
}
public abstract List<IMedium> getMediums() throws Exception;
@Override
protected List<IMedium> work(Void...params) throws Exception {
List<IMedium> mediums = getMediums();
for(IMedium m : mediums) {
m.getName();
}
return mediums;
}
@Override
protected void onSuccess(final List<IMedium> result) {
super.onSuccess(result);
final CharSequence []items = new CharSequence[result.size()];
for(int i=0; i<result.size(); i++)
items[i] = result.get(i).getName();
new AlertDialog.Builder(getActivity())
.setTitle("Select Medium")
.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
new MountTask(controller, deviceType).execute(result.get(item));
}
}).show();
}
}
/**
* List mountable mediums
*/
class ListDVDMediumsTask extends ActionBarTask<Void, List<IMedium>> {
private IStorageController controller;
public ListDVDMediumsTask(IStorageController controller) {
super(getSherlockActivity(),_machine.getAPI());
this.controller=controller;
}
@Override
protected List<IMedium> work(Void...params) throws Exception {
List<IMedium> mediums = _vmgr.getVBox().getHost().getDVDDrives();
mediums.addAll( _vmgr.getVBox().getDVDImages() );
for(IMedium m : mediums) {
m.getName(); m.getHostDrive();
}
return mediums;
}
@Override
protected void onSuccess(final List<IMedium> result) {
super.onSuccess(result);
final CharSequence []items = new CharSequence[result.size()+1];
for(int i=0; i<result.size(); i++) {
IMedium m = result.get(i);
items[i] = (m.getHostDrive() ? "Host Drive " : "") + m.getName();
}
items[items.length-1] = "No Disc";
new AlertDialog.Builder(getActivity())
.setTitle("Select Disk")
.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
new MountTask(controller, DeviceType.DVD).execute(items[item].equals("No Disc") ? null : result.get(item));
}
}).show();
}
}
/**
* Move the medium to a different slot within controller.
*/
class MountTask extends DialogTask<IMedium, IMediumAttachment> {
private IStorageController controller;
private DeviceType deviceType;
public MountTask(IStorageController controller, DeviceType type) {
super(getSherlockActivity(), _machine.getAPI(), R.string.progress_mounting_medium);
this.controller = controller;
this.deviceType = type;
}
@Override
protected IMediumAttachment work(IMedium...params) throws Exception {
IMedium medium = params[0];
IMediumAttachment attachment = new IMediumAttachment();
int devicesPerPort = controller.getMaxDevicesPerPortCount();
for(int i=0; i<controller.getMaxPortCount(); i++) {
for(int j=0; j<devicesPerPort; j++) {
boolean isUsed = false;
for(IMediumAttachment a : _dataListMap.get(controller)) {
if(a.getPort()==i && a.getDevice()==j) {
isUsed=true;
break;
}
}
if(!isUsed) {
attachment.setPort(i);
attachment.setDevice(j);
}
}
}
Log.d(TAG, "Attaching to slot: " + attachment.getSlot());
_machine.attachDevice(controller.getName(), attachment.getPort(), attachment.getDevice(), deviceType, medium);
attachment.setMedium(medium);
attachment.setType(deviceType);
attachment.setController(controller.getName());
return attachment;
}
@Override
protected void onSuccess(IMediumAttachment result) {
super.onSuccess(result);
Utils.toastShort(getContext(), "Attached medium to slot " + result.getSlot());
_data.put(controller, result);
_dataListMap.get(controller).add(result);
_listAdapter.notifyDataSetChanged();
}
}
private class ItemAdapter extends BaseExpandableListAdapter {
private final LayoutInflater mInflater;
private List<IStorageController> mControllers;
private ListMultimap<IStorageController, IMediumAttachment> mData;
private Map<IStorageController, List<IMediumAttachment>> mDataListMap;
public ItemAdapter(Context context, List<IStorageController> controllers, ListMultimap<IStorageController, IMediumAttachment> data) {
mInflater = LayoutInflater.from(context);
mData = data;
mControllers = controllers;
}
// public ItemAdapter(Context context, List<IStorageController> controllers, Map<IStorageController, List<IMediumAttachment>> data) {
// mInflater = LayoutInflater.from(context);
// mDataListMap = data;
// mControllers = controllers;
// }
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
for(int i=0; i<mControllers.size(); i++)
_listView.expandGroup(i);
}
@Override
public int getGroupCount() {
return mControllers.size();
}
@Override
public Object getGroup(int groupPosition) {
return mControllers.get(groupPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public int getChildrenCount(int groupPosition) {
if(mData!=null)
return mData.get(mControllers.get(groupPosition)).size();
else
return mDataListMap.get(mControllers.get(groupPosition)).size();
}
@Override
public Object getChild(int groupPosition, int childPosition) {
if(mData!=null)
return mData.get(mControllers.get(groupPosition)).get(childPosition);
else
return mDataListMap.get(mControllers.get(groupPosition)).get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
final IStorageController controller = (IStorageController)getGroup(groupPosition);
convertView = mInflater.inflate(R.layout.settings_storage_controller_list_item, parent, false);
convertView.setTag((TextView)convertView.findViewById(android.R.id.text1));
TextView text1 = (TextView)convertView.getTag();
text1.setText(controller.getName());
LinearLayout linear = (LinearLayout)convertView;
for(DeviceType type : _systemProperties.getDeviceTypesForStorageBus(controller.getBus())) {
ImageButton button = new ImageButton(getActivity());
button.setFocusable(false);
if(type.equals(DeviceType.HARD_DISK)) {
button.setImageResource(VBoxApplication.getInstance().getDrawable(R.drawable.ic_menu_hdd_add));
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new ListMediumsTask(controller, DeviceType.HARD_DISK) {
@Override
public List<IMedium> getMediums() throws Exception {
return _vmgr.getVBox().getHardDisks();
}
}.execute();
}
});
} else if(type.equals(DeviceType.DVD)) {
button.setImageResource(VBoxApplication.getInstance().getDrawable(R.drawable.ic_menu_dvd_add));
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new ListDVDMediumsTask(controller).execute();
}
});
}
linear.addView(button, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
// text1.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
final IMediumAttachment attachment = (IMediumAttachment)getChild(groupPosition, childPosition);
if(convertView==null) {
convertView = mInflater.inflate(R.layout.simple_selectable_list_item, parent, false);
convertView.setTag((TextView)convertView.findViewById(android.R.id.text1));
}
TextView text1 = (TextView)convertView.getTag();
text1.setText(attachment.getMedium()!=null ? attachment.getMedium().getBase().getName() : "Empty");
if(attachment.getType().equals(DeviceType.HARD_DISK))
text1.setCompoundDrawablesWithIntrinsicBounds(VBoxApplication.getInstance().getDrawable(R.drawable.ic_button_hdd), 0, 0, 0);
if(attachment.getType().equals(DeviceType.DVD))
text1.setCompoundDrawablesWithIntrinsicBounds(VBoxApplication.getInstance().getDrawable(R.drawable.ic_button_dvd), 0, 0, 0);
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
_machine = (IMachine)getArguments().getParcelable(IMachine.BUNDLE);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.settings_storage_tree, container, false);
_listView = (ExpandableListView)view.findViewById(R.id.storage_tree);
_listView.setOnGroupClickListener(new OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
parent.expandGroup(groupPosition);
parent.setSelectedGroup(groupPosition);
if(_controllerListener!=null)
_controllerListener.onStorageControllerClicked((IStorageController)_listAdapter.getGroup(groupPosition));
return true;
}
});
_listView.setOnChildClickListener(new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
parent.setSelectedChild(groupPosition, childPosition, true);
if(_mediumListener!=null)
_mediumListener.onMediumAttachmentClicked((IMediumAttachment)_listAdapter.getChild(groupPosition, childPosition));
return true;
}
});
registerForContextMenu(_listView);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getParentFragment() instanceof OnStorageControllerClickedListener)
_controllerListener = (OnStorageControllerClickedListener)getParentFragment();
if(getParentFragment() instanceof OnMediumAttachmentClickedListener)
_mediumListener = (OnMediumAttachmentClickedListener)getParentFragment();
}
@Override
public void onStart() {
super.onStart();
if(_dataListMap==null && _data==null)
new LoadDataTask().execute(_machine);
else {
_listAdapter = new ItemAdapter(getSherlockActivity(), _controllers, _data);
_listView.setAdapter(_listAdapter);
for(int i=0; i<_dataListMap.keySet().size(); i++)
_listView.expandGroup(i);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.storage_actions, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.menu_add_controller:
List<CharSequence> itemList = new ArrayList<CharSequence>(5);
ChipsetType chipset = _machine.getChipsetType();
for(StorageBus bus : Utils.removeNull(StorageBus.values())) {
if(getNumStorageControllersOfType(bus)<_systemProperties.getMaxInstancesOfStorageBus(chipset, bus))
itemList.add(bus.toString());
}
final CharSequence[] items = itemList.toArray(new CharSequence[itemList.size()]);
new AlertDialog.Builder(getActivity())
.setTitle("Controller Type")
.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
new AddControllerTask().execute(StorageBus.fromValue(items[item].toString()));
}
}).show();
return true;
case R.id.menu_refresh:
new LoadDataTask().execute(_machine);
return true;
}
return false;
}
private int getNumStorageControllersOfType(StorageBus bus) {
int numControllers = 0;
for(IStorageController controller : _dataListMap.keySet()) {
if(controller.getBus().equals(bus))
numControllers++;
}
return numControllers;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.add(Menu.NONE, R.id.menu_delete, Menu.NONE, R.string.delete);
}
@Override
public boolean onContextItemSelected(android.view.MenuItem item) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) item.getMenuInfo();
int groupNum = ExpandableListView.getPackedPositionGroup(info.packedPosition);
switch(ExpandableListView.getPackedPositionType(info.packedPosition)) {
case ExpandableListView.PACKED_POSITION_TYPE_GROUP:
IStorageController controller = (IStorageController)_listAdapter.getGroup(groupNum);
new DeleteControllerTask().execute(controller);
break;
case ExpandableListView.PACKED_POSITION_TYPE_CHILD:
int childNum = ExpandableListView.getPackedPositionChild(info.packedPosition);
IMediumAttachment attachment = (IMediumAttachment)_listAdapter.getChild(groupNum, childNum);
new DetachMediumTask().execute(attachment);
break;
}
return false;
}
}